package com.neusoft.databus.agent.parser.modbus;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.springframework.util.StringUtils;

import com.neusoft.databus.common.bean.MetricBean;
import com.neusoft.databus.common.utils.DigitalUtils;

/**
 * Modbus TCP parser <br >
 * MBAP: Modbus application protocol header above TCP/IP
 *
 * @author naxiang
 */
public class ModBusTCPParser extends ModBusParser {

    private static final String PROTOCOL_ID = "0000";
    private static final String SEND_DATA_LENGTH = "0006";

    private static AtomicInteger transactionId = new AtomicInteger();
    private final Map<String, Integer> transactionMaps = new HashMap<>();

    /**
     * 19 B2 00 00 00 06 06 03 00 27 00 02 <br >
     * 19 B2 00 00 00 07 06 03 04 00 00 00 00 <br >
     * "19 B2 00 00 00 07 06 03 04" is logical header
     */
    @Override
    public int getResponseHeaderLength() {
        return 9;
    }

    @Override
    public byte[] generateReq(String slave, MetricBean metricBean) {
        final String commandCode = metricBean.getCommand();
        return this.generateReq(slave, commandCode);
    }

    public byte[] generateReq(String slave, String commandCode) {
        if (StringUtils.isEmpty(commandCode) || StringUtils.isEmpty(commandCode.trim())) {
            return null;
        }
        if (!this.transactionMaps.containsKey(slave + commandCode)) {
            final int currentTransaction = ModBusTCPParser.transactionId.incrementAndGet();
            this.transactionMaps.put(slave + commandCode, currentTransaction);
            ModBusTCPParser.transactionId.compareAndSet((Short.MAX_VALUE * 2) + 1, 0);
        }

        final StringBuilder reqHex = new StringBuilder();
        reqHex.append(String.format("%04x", this.transactionMaps.get(slave + commandCode) % 65535))
                .append(ModBusTCPParser.PROTOCOL_ID).append(ModBusTCPParser.SEND_DATA_LENGTH).append(slave)
                .append(commandCode);

        final byte[] data = DigitalUtils.hexStringToBytes(reqHex.toString());
        return data;
    }

    /**
     * because modbus tcp is only supported by socket. so is never not be used;
     */
    @Override
    public int expectedResDataLength(byte[] req) {
        final byte functionCode = req[7];
        final int registerSize = DigitalUtils.bytesToUShort(new byte[] {req[10], req[11] }, 0);
        int expected = this.getResponseHeaderLength() + (registerSize * 2);
        switch (functionCode) {
        case 0x01:
        case 0x02:
            expected = this.getResponseHeaderLength() + ((Double) Math.ceil(registerSize / 8.0)).intValue();
            break;
        default:
            break;
        }
        return expected;
    }

    @Override
    public boolean validateResult(byte[] packet) {
        if (null == packet) {
            return false;
        }
        return true;
    }

}
